#!/usr/bin/env node

/**
 * 检查已预约数据是否被官方取消
 *
 * 功能：
 * - 查询所有状态为 'booked' 的预约记录
 * - 通过查询预约方法（queryByRef 或 queryById）检查是否被官方取消
 * - 如果查询返回 NO_RECORD，将状态更新为 'cancelled'
 * - 使用并发处理以提高效率，但限制并发数避免误操作
 *
 * 使用方法：
 *   方式1：使用 tsx（推荐，支持 TypeScript）
 *     npx tsx --tsconfig app/tsconfig.node.json app/scripts/check-cancelled-appointments.mjs
 *     npx tsx --tsconfig app/tsconfig.node.json app/scripts/check-cancelled-appointments.mjs --concurrency 3
 *
 *   方式2：先编译后运行（如果方式1失败）
 *     npm run build
 *     node app/scripts/check-cancelled-appointments.mjs
 *
 *   方式3：指定数据库路径
 *     npx tsx --tsconfig app/tsconfig.node.json app/scripts/check-cancelled-appointments.mjs --db-path "C:\path\to\app.db" --concurrency 5
 *
 * 参数说明：
 *   --concurrency <数字>  : 并发查询数量（默认 3，范围 1-10）
 *   --db-path <路径>      : 指定数据库文件路径（可选）
 *
 * 注意事项：
 *   - 脚本会实际查询官方系统，请合理设置并发数避免被封禁
 *   - 只有查询返回 NO_RECORD 的记录才会被更新为 cancelled
 *   - 网络错误或系统繁忙时不会更新状态，避免误操作
 */

import { existsSync } from 'node:fs'
import { readFile as fsReadFile, writeFile as fsWriteFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const projectRoot = join(__dirname, '..')

// 注册 TypeScript 路径别名（用于 tsx 运行时）
// tsx 应该会自动处理路径别名，但如果不行，这里提供一个备用方案
async function registerPathAliases() {
  try {
    // 尝试使用 tsconfig-paths（CommonJS 风格，但可能不适用于 ES 模块）
    // 注意：tsx 通常会自动处理 tsconfig.json 中的路径别名
    // 如果还是失败，可能需要先编译代码再运行
    const tsconfigPaths = await import('tsconfig-paths')
    const tsConfigPath = join(projectRoot, 'tsconfig.node.json')
    if (existsSync(tsConfigPath)) {
      // 注意：register 可能需要在模块加载前调用
      // 对于 ES 模块，这可能不起作用
      const result = tsconfigPaths.register({
        baseUrl: projectRoot,
        paths: {
          '@shared/*': ['src/shared/*'],
          '@renderer/*': ['src/renderer/src/*'],
        },
      })
      if (result) {
        console.log('✅ 路径别名已注册')
        return true
      }
    }
  }
  catch {
    // tsconfig-paths 可能不适用于 ES 模块，这是正常的
    // tsx 应该会自动处理路径别名
  }
  return false
}

async function main() {
  // 首先注册路径别名
  await registerPathAliases()

  console.log('='.repeat(60))
  console.log('检查已预约数据是否被官方取消')
  console.log('='.repeat(60))
  console.log()

  // 解析命令行参数
  const args = process.argv.slice(2)
  let specifiedDbPath = null
  let concurrency = 3 // 默认并发数为3，避免请求过快
  let onlyIdsRaw = null
  let onlyIds = null
  let retries = 2
  let retryDelayMs = 400

  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--db-path' && i + 1 < args.length) {
      specifiedDbPath = args[i + 1]
    }
    else if (args[i] === '--concurrency' && i + 1 < args.length) {
      concurrency = Math.max(1, Math.min(10, Number.parseInt(args[i + 1], 10) || 3))
    }
    else if (args[i] === '--ids' && i + 1 < args.length) {
      onlyIdsRaw = String(args[i + 1] || '')
    }
    else if (args[i] === '--ids-file' && i + 1 < args.length) {
      onlyIdsRaw = `@${String(args[i + 1] || '')}`
    }
    else if (args[i] === '--retries' && i + 1 < args.length) {
      retries = Math.max(0, Math.min(10, Number.parseInt(args[i + 1], 10) || 2))
    }
    else if (args[i] === '--retry-delay-ms' && i + 1 < args.length) {
      retryDelayMs = Math.max(100, Math.min(10000, Number.parseInt(args[i + 1], 10) || 400))
    }
  }

  console.log(`并发数: ${concurrency}`)
  console.log()

  // 解析 onlyIds 参数（支持逗号分隔或文件）
  if (onlyIdsRaw) {
    try {
      if (onlyIdsRaw.startsWith('@')) {
        const path = onlyIdsRaw.slice(1)
        const content = await fsReadFile(path, 'utf-8')
        onlyIds = content
          .split(/\r?\n|,|\s+/g)
          .map(s => Number.parseInt(String(s).trim(), 10))
          .filter(n => Number.isFinite(n))
      }
      else {
        onlyIds = String(onlyIdsRaw)
          .split(',')
          .map(s => Number.parseInt(String(s).trim(), 10))
          .filter(n => Number.isFinite(n))
      }
    }
    catch {
      onlyIds = null
    }
  }

  // 初始化数据库和服务
  console.log('正在初始化数据库和服务...')

  // 如果用户指定了数据库路径，设置环境变量让数据库模块使用它
  if (specifiedDbPath) {
    if (existsSync(specifiedDbPath)) {
      // 设置环境变量：覆盖数据目录，供 initializeDatabase()/getDataPath() 使用
      const dataDir = dirname(specifiedDbPath)
      process.env.AUTO_TOOLS_DATA_PATH = dataDir
      console.log(`使用指定的数据库文件: ${specifiedDbPath}`)
    }
    else {
      console.error(`❌ 指定的数据库文件不存在: ${specifiedDbPath}`)
      process.exit(1)
    }
  }
  else {
    // 尝试查找数据库文件（用于验证）
    const possibleDbPaths = []

    // 如果是Windows，优先使用生产环境路径
    if (process.platform === 'win32') {
      // 优先使用生产环境实际路径
      possibleDbPaths.push('C:\\Users\\Administrator\\AppData\\Roaming\\AutoTools\\data\\app.db')

      const appData = process.env.APPDATA || process.env.LOCALAPPDATA
      if (appData) {
        // 使用AutoTools（生产环境实际使用的路径）
        possibleDbPaths.push(join(appData, 'AutoTools', 'data', 'app.db'))
        // 兼容其他可能的路径
        possibleDbPaths.push(join(appData, 'auto-tools', 'data', 'app.db'))
      }
    }

    // 尝试使用 getDataPath()（如果在 Electron 环境中）
    try {
      const { getDataPath } = await import('../src/main/config/index.js')
      const dataPath = getDataPath()
      possibleDbPaths.push(join(dataPath, 'app.db'))
    }
    catch {
      // 忽略导入失败
    }

    // 最后尝试开发环境：项目目录下的数据库
    possibleDbPaths.push(join(projectRoot, 'prisma', 'data', 'app.db'))
    possibleDbPaths.push(join(projectRoot, '..', 'prisma', 'data', 'app.db'))

    // 查找存在的数据库文件（按优先级顺序）
    console.log('正在查找数据库文件（按优先级）...')
    let found = false
    let foundDbPath = null
    for (const p of possibleDbPaths) {
      if (existsSync(p)) {
        console.log(`✅ 找到数据库文件: ${p}`)
        found = true
        foundDbPath = p
        break
      }
      else {
        console.log(`  ⚠️  不存在: ${p}`)
      }
    }

    if (!found) {
      console.error('❌ 未找到数据库文件')
      console.error()
      console.error('可用的解决方案：')
      console.error('1. 使用 --db-path 参数指定数据库路径')
      console.error('   例如: npx tsx app/scripts/check-cancelled-appointments.mjs --db-path "C:\\Users\\Administrator\\AppData\\Roaming\\AutoTools\\data\\app.db"')
      console.error('2. 检查数据库文件是否存在')
      console.error()
      process.exit(1)
    }

    // 覆盖数据目录供数据库模块使用
    try {
      if (foundDbPath) {
        const dataDir = dirname(foundDbPath)
        process.env.AUTO_TOOLS_DATA_PATH = dataDir
      }
    }
    catch {}
  }

  // 初始化配置和日志系统（服务需要这些）
  console.log('正在初始化配置和日志系统...')
  try {
    // 1. 初始化配置
    const { loadConfig } = await import('../src/main/config/index.js')
    await loadConfig()
    console.log('✅ 配置初始化成功')

    // 2. 初始化日志系统
    const { setupLogger } = await import('../src/main/logger/index.js')
    await setupLogger()
    console.log('✅ 日志系统初始化成功')

    // 3. 初始化数据库（使用数据库模块的初始化函数）
    const { initializeDatabase } = await import('../src/main/db/index.js')
    await initializeDatabase()
    console.log('✅ 数据库初始化成功')
  }
  catch (error) {
    console.error('❌ 初始化失败:', error.message)
    console.error('错误详情:', error)
    process.exit(1)
  }

  // 初始化服务
  // 注意：这些服务需要完整的应用环境（配置和日志已初始化）
  let queryService = null
  let appointmentService = null

  try {
    // 尝试多种导入路径（适应不同的构建环境）
    let serviceModule = null
    let appointmentModule = null

    // 方法1：尝试直接导入源文件（如果使用 tsx/ts-node）
    try {
      serviceModule = await import('../src/main/services/tdabs-request-service.ts')
      appointmentModule = await import('../src/main/services/appointment-service.ts')
    }
    catch {
      // 方法2：尝试导入编译后的文件
      try {
        serviceModule = await import('../dist/main/services/tdabs-request-service.js')
        appointmentModule = await import('../dist/main/services/appointment-service.js')
      }
      catch {
        // 方法3：尝试导入 .js 扩展名（electron-vite 可能生成）
        try {
          serviceModule = await import('../src/main/services/tdabs-request-service.js')
          appointmentModule = await import('../src/main/services/appointment-service.js')
        }
        catch (err) {
          throw new Error(`无法导入服务模块: ${err.message}`)
        }
      }
    }

    const { getTdabsRequestService } = serviceModule
    const { getAppointmentService } = appointmentModule

    // 初始化服务（现在配置和日志都已准备好）
    queryService = getTdabsRequestService()
    appointmentService = getAppointmentService()
    console.log('✅ 服务初始化成功')
  }
  catch (error) {
    console.error('❌ 服务初始化失败:', error.message)
    console.error()
    console.error('可能的解决方案：')
    console.error('1. 如果是在开发环境，请先运行: npm run build')
    console.error('2. 或者使用 tsx 运行: npx tsx --tsconfig app/tsconfig.node.json app/scripts/check-cancelled-appointments.mjs')
    console.error('3. 确保在应用目录下运行此脚本')
    console.error()
    console.error('错误详情:', error)
    try {
      const { closeDatabase } = await import('../src/main/db/index.js')
      await closeDatabase()
    }
    catch {
      // 忽略关闭数据库时的错误
    }
    process.exit(1)
  }

  // 获取数据库实例（用于查询）
  const { getDatabase } = await import('../src/main/db/index.js')
  const prisma = getDatabase()

  try {
    // 查询预约记录（默认查询已预约；若指定 --ids 则优先按IDs查询）
    if (Array.isArray(onlyIds) && onlyIds.length > 0) {
      console.log(`按ID清单检查，共 ${onlyIds.length} 条`)
    }
    else {
      console.log('正在查询已预约的记录...')
    }

    const whereCond = (() => {
      if (Array.isArray(onlyIds) && onlyIds.length > 0)
        return { id: { in: onlyIds } }
      return { appointment_status: 'booked' }
    })()

    const bookedAppointments = await prisma.appointment_management.findMany({
      where: whereCond,
      select: {
        id: true,
        reference_number: true,
        passport_number: true,
        license_number: true,
        full_name: true,
        appointment_date: true,
        appointment_time: true,
      },
      orderBy: {
        updated_at: 'desc',
      },
    })

    const total = bookedAppointments.length
    console.log(`找到 ${total} 条记录待检查`)
    console.log()

    if (total === 0) {
      console.log('没有需要检查的记录')
      const { closeDatabase } = await import('../src/main/db/index.js')
      await closeDatabase()
      return
    }

    // 安全提示
    console.log('⚠️  重要提示：')
    console.log('  - 此脚本将查询官方系统检查预约状态')
    console.log('  - 只有确认返回 NO_RECORD 的记录才会被更新为 cancelled')
    console.log('  - 网络错误或系统繁忙时不会更新状态，确保不会误操作')
    console.log()
    console.log(`准备检查 ${total} 条记录，并发数 ${concurrency}`)
    console.log('按 Ctrl+C 可以随时取消')
    console.log()

    // 统计信息
    let checked = 0
    let cancelled = 0
    let stillValid = 0
    let queryFailed = 0
    const errors = []

    // 判定是否为瞬时错误（可重试）
    const isTransient = (msg) => {
      if (!msg)
        return false
      const m = String(msg)
      return /(系统繁忙|超时|timeout|网络|ECONN|EAI_AGAIN|暂时|重试|未能获取|no\s*record\s*yet)/i.test(m)
    }

    // 并发处理函数
    const processAppointment = async (apt) => {
      try {
        let queryResult = null

        const doQueryOnce = async () => {
          if (apt.reference_number && String(apt.reference_number).trim().length >= 6) {
            const refLast6 = String(apt.reference_number).trim().slice(-6)
            console.log(`[${apt.id}] 使用参考编号查询: ${refLast6} (${apt.full_name || 'N/A'})`)
            return await queryService.queryByRef({ refLast6 })
          }
          if (apt.passport_number && apt.license_number) {
            console.log(`[${apt.id}] 使用证件号+驾照号查询: ${apt.full_name || 'N/A'}`)
            return await queryService.queryById({
              passport: String(apt.passport_number).trim(),
              license: String(apt.license_number).trim(),
            })
          }
          console.log(`[${apt.id}] ⚠️  缺少查询所需信息（参考编号或证件号+驾照号），跳过`)
          return { success: false, error: 'MISSING_INPUT' }
        }

        // 带重试的查询
        let attempt = 0
        while (true) {
          attempt++
          queryResult = await doQueryOnce()
          if (queryResult && queryResult.success)
            break
          const errMsg = queryResult?.error || ''
          if (attempt > Math.max(0, retries) || !isTransient(errMsg))
            break
          const delay = Math.floor(retryDelayMs * (1.6 ** (attempt - 1)) + Math.random() * 200)
          console.log(`[${apt.id}] 重试第 ${attempt} 次，原因: ${errMsg || 'unknown'}，等待 ${delay}ms`)
          await new Promise(r => setTimeout(r, delay))
        }

        // 检查查询结果
        if (!queryResult || !queryResult.success) {
          if (queryResult?.error === 'NO_RECORD') {
            // 查询返回 NO_RECORD，说明预约已被官方取消
            // 这是一个明确的信号，可以安全地更新状态
            console.log(`[${apt.id}] ❌ 查询返回 NO_RECORD，预约已被取消`)

            // 再次确认：查询数据库中的当前状态，避免并发更新冲突
            const currentApt = await prisma.appointment_management.findUnique({
              where: { id: apt.id },
              select: { appointment_status: true },
            })

            // 只有当状态仍然是 'booked' 时才更新，避免重复更新
            if (currentApt && currentApt.appointment_status === 'booked') {
              await appointmentService.updateAppointment(apt.id, {
                appointment_status: 'cancelled',
                error_message: '官方系统查询返回 NO_RECORD，预约已被取消',
              })
              cancelled++
              return { cancelled: true }
            }
            else {
              console.log(`[${apt.id}] ⚠️  状态已变更，跳过更新（当前状态: ${currentApt?.appointment_status || 'unknown'}）`)
              return { skipped: true }
            }
          }
          else {
            // 其他查询错误（网络错误、系统繁忙等），不更新状态
            // 这是安全措施：只有在明确返回 NO_RECORD 时才更新
            const errorMsg = queryResult?.error || '查询失败'
            console.log(`[${apt.id}] ⚠️  查询失败: ${errorMsg}，跳过更新（安全措施）`)
            queryFailed++
            errors.push({ id: apt.id, error: errorMsg })
            return { queryFailed: true }
          }
        }
        else {
          // 查询成功，说明预约仍然有效
          const hasRef = queryResult.data?.parsed?.reference_number
          console.log(`[${apt.id}] ✅ 预约仍然有效${hasRef ? ` (参考编号: ${hasRef})` : ''}`)
          stillValid++
          return { stillValid: true }
        }
      }
      catch (error) {
        const errorMsg = error?.message || String(error)
        console.log(`[${apt.id}] ❌ 处理异常: ${errorMsg}`)
        queryFailed++
        errors.push({ id: apt.id, error: errorMsg })
        return { error: true }
      }
    }

    // 并发控制：使用 Promise.allSettled 分批处理
    console.log('开始检查预约状态...')
    console.log()

    for (let i = 0; i < bookedAppointments.length; i += concurrency) {
      const batch = bookedAppointments.slice(i, i + concurrency)
      const batchPromises = batch.map(apt => processAppointment(apt))

      await Promise.allSettled(batchPromises)
      checked += batch.length

      // 批次间添加延迟，避免请求过于频繁
      if (i + concurrency < bookedAppointments.length) {
        const delay = 500 + Math.floor(Math.random() * 500) // 500-1000ms 随机延迟
        await new Promise(resolve => setTimeout(resolve, delay))
      }

      // 显示进度
      const progress = Math.floor((checked / total) * 100)
      console.log(`进度: ${checked}/${total} (${progress}%)`)
      console.log()
    }

    // 输出统计结果
    console.log('='.repeat(60))
    console.log('检查完成')
    console.log('='.repeat(60))
    console.log(`总记录数: ${total}`)
    console.log(`已检查: ${checked}`)
    console.log(`✅ 仍然有效: ${stillValid}`)
    console.log(`❌ 已取消（已更新）: ${cancelled}`)
    console.log(`⚠️  查询失败（未更新）: ${queryFailed}`)
    console.log()

    if (errors.length > 0) {
      console.log('查询失败的记录:')
      errors.slice(0, 10).forEach((err) => {
        console.log(`  - ID ${err.id}: ${err.error}`)
      })
      if (errors.length > 10) {
        console.log(`  ... 还有 ${errors.length - 10} 条失败记录`)
      }
      console.log()

      // 将失败ID输出到文件，便于二次重试
      try {
        const ts = new Date().toISOString().replace(/[:.]/g, '').slice(0, 15)
        const failPath = `failed-ids-${ts}.txt`
        const content = errors.map(e => String(e.id)).join('\n')
        await fsWriteFile(failPath, content, 'utf-8')
        console.log(`已写出失败ID到文件: ${failPath}`)
        console.log('可用以下命令仅重试这些ID：')
        console.log(`npx tsx app/scripts/check-cancelled-appointments.mjs --ids-file "${failPath}" --retries ${Math.max(2, retries)} --retry-delay-ms ${retryDelayMs}`)
      }
      catch {}
    }

    if (cancelled > 0) {
      console.log(`✅ 成功将 ${cancelled} 条记录的状态更新为 'cancelled'`)
    }

    const { closeDatabase } = await import('../src/main/db/index.js')
    await closeDatabase()
  }
  catch (error) {
    console.error('❌ 执行失败:', error)
    try {
      const { closeDatabase } = await import('../src/main/db/index.js')
      await closeDatabase()
    }
    catch {
      // 忽略关闭数据库时的错误
    }
    process.exit(1)
  }
}

main().catch((error) => {
  console.error('❌ 未处理的错误:', error)
  process.exit(1)
})
